package de.dhbw.karlsruhe.picsim.gui.control;

import de.dhbw.karlsruhe.picsim.codeimport.LSTFile;
import de.dhbw.karlsruhe.picsim.codeimport.LSTFileImpl;
import de.dhbw.karlsruhe.picsim.gui.CommandRegister;
import de.dhbw.karlsruhe.picsim.gui.CommandRegisterImpl;
import de.dhbw.karlsruhe.picsim.gui.VisualisationFactory;
import de.dhbw.karlsruhe.picsim.gui.VisualisationFactoryImpl;
import de.dhbw.karlsruhe.picsim.model.PicTime;
import de.dhbw.karlsruhe.picsim.model.actions.impl.ActionFactoryImpl;
import de.dhbw.karlsruhe.picsim.model.cpu.*;
import de.dhbw.karlsruhe.picsim.model.cpu.execute.ActionExecutor;
import de.dhbw.karlsruhe.picsim.model.cpu.execute.ActionExecutorImpl;
import de.dhbw.karlsruhe.picsim.model.cpu.impl.*;
import de.dhbw.karlsruhe.picsim.model.decode.Decoder;
import de.dhbw.karlsruhe.picsim.model.decode.DecoderImpl;
import de.dhbw.karlsruhe.picsim.model.io.External;
import de.dhbw.karlsruhe.picsim.model.io.ExternalBackend;
import de.dhbw.karlsruhe.picsim.model.io.ExternalInterrupts;
import de.dhbw.karlsruhe.picsim.model.io.ExternalPins;
import de.dhbw.karlsruhe.picsim.model.memory.*;
import de.dhbw.karlsruhe.picsim.model.memory.impl.*;
import de.dhbw.karlsruhe.picsim.model.timer.*;
import de.dhbw.karlsruhe.picsim.rs232.ComManager;
import de.dhbw.karlsruhe.picsim.rs232.ProtocolLogic;
import de.dhbw.karlsruhe.picsim.rs232.ProtocolLogicImpl;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jetbrains.annotations.NotNull;

import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.io.*;
import java.io.File;

/**
 * Created by IntelliJ IDEA.
 * User: goesen
 * Date: Apr 18, 2010
 * Time: 2:18:33 PM
 */
@SuppressWarnings({"CloneableClassWithoutClone"})
public final class ImportLstAction extends AbstractAction {
    private static final Log log = LogFactory.getLog(ImportLstAction.class);
    private final JFrame parent;
    private final JButton stepButton;
    private final JButton playButton;
    private final JButton pauseButton;
    private final JButton resetButton;
    private final ComManager comManager;
    private static final String LST_FILE_ENCODING = "ISO-8859-1";

    public ImportLstAction(final JFrame parent, final JButton stepButton, final JButton playButton,
                           final JButton pauseButton,
                           final JButton resetButton, ComManager comManager) {
        super("Öffne Lst Datei");
        this.parent = parent;
        this.stepButton = stepButton;
        this.playButton = playButton;
        this.pauseButton = pauseButton;
        this.resetButton = resetButton;
        this.comManager = comManager;
    }

    @Override
    public void actionPerformed(final ActionEvent e) {
        final JFileChooser chooser = new JFileChooser();
        chooser.setFileFilter(new FileNameExtensionFilter("Lst Dateien", "lst"));
        final int result = chooser.showOpenDialog(parent);
        if (result == JFileChooser.APPROVE_OPTION) {
            try {
                importLST(chooser.getSelectedFile());
            } catch (IOException e1) {
                log.error(e1);
            }
        }
    }

    void importLST(@NotNull final File file) throws IOException {
        final FileInputStream stream = new FileInputStream(file);
        InputStreamReader streamReader;
        try {
            streamReader = new InputStreamReader(stream, LST_FILE_ENCODING);
        } catch (UnsupportedEncodingException e) {
            log.error("Charset " + LST_FILE_ENCODING + " not found, continue with System-default", e);
            streamReader = new InputStreamReader(stream);
        }
        final BufferedReader bufferedReader = new BufferedReader(streamReader);
        final LSTFile lstFile = new LSTFileImpl(bufferedReader);
        bufferedReader.close();

        //erzeuge einen neuen Stack auf dem beim call addressen gepushed werden
        final DirectAddressAbleStack stack = new StackFacadeImpl();
        //im ProgramMemory befinden sich die Opcodes
        final ProgramMemory programMemory = new ProgramMemoryImpl(lstFile.getLines().iterator());
        //was der Pic von innen an die äußeren Pins liegt
        final ExternalBackend backend = new ExternalBackend();
        //Diese Klasse repräsentiert die Äußeren pins
        final External external = new ExternalPins(backend);
        final HiddenRegisters hiddenRegisters = new HiddenRegistersImpl();
        final FileFactory fileFactory = new FileFactoryImpl(backend, hiddenRegisters);
        final FileMemoryMutable fileMemory = FileMemoryImpl.create(fileFactory);
        final FileMutable wRegister = new FileImpl();
        final Decoder decoder = new DecoderImpl();

        final ProtocolLogic protocolLogic = new ProtocolLogicImpl(fileFactory.getPortAFile(),
                                                                  fileFactory.getPortBFile(),
                                                                  fileFactory.getTrisAFile(),
                                                                  fileFactory.getTrisBFile(), comManager, external);
        final PicInstructionFacade instructionFacade = new PicInstructionFacadeImpl(wRegister, fileMemory,
                                                                                    new ActionFactoryImpl(),
                                                                                    protocolLogic);

        final FileMutable intCon = fileMemory.getFile(FileAddressImpl.intConAddress());
        final FileMutable optionFile = fileMemory.getFile(FileAddressImpl.optionAddress());
        final InterruptFacade interruptFacade = new InterruptHolderImpl(intCon);
        //hängt einen Listener an die externen Ports an, um bei Änderungen Interrupts auszulösen
        external.addListener(new ExternalInterrupts(interruptFacade, optionFile));
        final ProgramCounterMutable programCounter = new ProgramCounterImpl(hiddenRegisters,
                                                                            fileMemory.getFile(
                                                                                    FileAddressImpl.programCounterLowAddress()));

        final InstructionPipeline instructionPipeline = new InstructionPipelineImpl(decoder, programMemory,
                                                                                    programCounter, intCon);

        final TimerFacade timerFacade = new TimerFacadeImpl(optionFile,
                                                            fileMemory.getFile(FileAddressImpl.timerAddress()),
                                                            external,
                                                            fileMemory.getFile(FileAddressImpl.statusAddress()),
                                                            interruptFacade);
        final CommandRegister guiData = new CommandRegisterImpl(programMemory, lstFile, programCounter);
        final ActionExecutor actionExecutor = new ActionExecutorImpl
                (fileMemory, wRegister, stack, programCounter, hiddenRegisters, instructionPipeline,
                 timerFacade);
        final Processor processor = new ProcessorImpl(actionExecutor, instructionFacade, instructionPipeline);

        final Counter counter = new CounterImpl();
        final PicTime picTime = new PicTime(processor, timerFacade, counter, protocolLogic);


        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                parent.setTitle("PicSim " + file.getAbsolutePath());
                resetButton.setAction(new ResetAction(ImportLstAction.this, file));
                final VisualisationFactory gui = new VisualisationFactoryImpl(guiData, fileMemory, wRegister, stack,
                                                                              counter);

                final JSplitPane jSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
                parent.setContentPane(jSplitPane);
                //jSplitPane.setLeftComponent(gui.getWRegisterVisualisation());

                final JComponent programMemory = gui.getProgramMemoryVisualisation();

                final EncircleAction encircle = new EncircleAction(gui, picTime);
                stepButton.setAction(encircle);

                final PlayAction play = new PlayAction(picTime, gui, pauseButton,
                                                       new DebugControl(programCounter, guiData));
                playButton.setAction(play);

                jSplitPane.setLeftComponent(programMemory);

                timerFacade.addWatchdogTimeOutListener(new ObservableTimer.TimeoutListener() {
                    @Override
                    public void timeoutOccurred() {
                        play.setPlay(false);
                        final JDialog dialog = new JDialog(parent);
                        dialog.setTitle("Watchdog abgelaufen");
                        dialog.setVisible(true);
                        dialog.setSize(new Dimension(300, 30));
                    }
                });


                final JComponent stack = gui.getStackVisualisation();
                final JPanel rightPanel = new JPanel(new GridLayout(0, 1));
                rightPanel.add(stack);
                rightPanel.add(gui.getWRegisterVisualisation());
                rightPanel.add(gui.getTimeVisualisation());
                rightPanel.setPreferredSize(new Dimension(200, 600));
                final Component centerSplit = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
                                                             gui.getDataMemoryVisualisation(),
                                                             rightPanel);
                jSplitPane.setRightComponent(centerSplit);


                jSplitPane.revalidate();
                parent.pack();
            }
        });
    }

}
